package com.nineclock.sign.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.threadlocal.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.sign.dto.SignInfoDTO;
import com.nineclock.sign.dto.SignPCListDTO;
import com.nineclock.sign.mapper.SignInfoMapper;
import com.nineclock.sign.pojo.SignInfo;
import com.nineclock.sign.service.SignInfoService;
import com.nineclock.sign.utils.MyColumnStyle;
import com.nineclock.system.dto.SysCompanyUserDTO;
import com.nineclock.system.dto.SysDepartmentDTO;
import com.nineclock.system.feign.SysUserFeign;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import com.nineclock.sign.dto.SignDTO;
import com.nineclock.sign.mapper.SignPictureMapper;
import com.nineclock.sign.pojo.SignPicture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.Date;

@Service
@Slf4j
public class SignInfoServiceImpl implements SignInfoService {

    @Autowired
    SysUserFeign sysUserFeign;

    @Autowired
    SignInfoMapper signInfoMapper;

    @Autowired
    SignPictureMapper signPictureMapper;

    @Autowired
    SysUserFeign userFeign;

    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 签到：服务端业务实现：
     * <p>
     * 1. 将签到数据保存到数据库
     * 2. 将签到的图片阿里云地址更新到签到图片表中
     * 3. 记录/更新用户当日打卡次数 ---> 考虑使用redis存储
     * 4. 记录/更新用户当月打卡次数 ---> 考虑使用redis存储
     *
     * @param signDTO
     */
    @Override
    public void insert(SignDTO signDTO) throws IOException {
        if (signDTO == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        log.info("1. 将签到数据保存到数据库");
        //封装基本数据
        signDTO.setCompanyId(CurrentUserHolder.get().getCompanyId());
        signDTO.setCompanyUserId(CurrentUserHolder.get().getCompanyUserId());
        signDTO.setCreateTime(new Date());
        //把日期转为字符串 "yyyy-MM-dd";
        signDTO.setCreateDate(DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_2));

        //把日期转为字符串 "yyyy-MM";
        signDTO.setCreateMonth(DateTimeUtil.dateToStr(new Date(), DateTimeUtil.TIME_FORMAT_4));

        //数据转换
        SignInfo signInfo = BeanHelper.copyProperties(signDTO, SignInfo.class);
        //把数据插入到数据库
        signInfoMapper.insert(signInfo);

        //获取图片信息
        String images = signDTO.getImages();
        if(images==null){
            throw new NcException(ResponseEnum.DATA_NOT_EXISTS);
        }
        log.info("从前端获取图片信息:"+images);
        String[] split = images.split(",");
        //遍历截取的图片信息
        for (String imgUrl : split) {
            SignPicture signPicture = new SignPicture();
            signPicture.setSignId(signInfo.getId());
            signPicture.setUrl(imgUrl);
            signPictureMapper.insert(signPicture);
        }

        //查询当天签到次数
        LambdaQueryWrapper<SignInfo> signInfoDate=new LambdaQueryWrapper<>();
        signInfoDate.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        signInfoDate.eq(SignInfo::getCompanyUserId,CurrentUserHolder.get().getCompanyUserId());
        signInfoDate.eq(SignInfo::getCreateDate,signDTO.getCreateDate());
        Integer day = signInfoMapper.selectCount(signInfoDate);
        String dayCount = String.valueOf(day);

        //存储当天的签到次数到redis
        if(dayCount==null){
            throw new NcException(ResponseEnum.DATA_NOT_EXISTS);
        }

        //设置key=当天日期+手机号
        String createDate = signDTO.getCreateDate()+CurrentUserHolder.get().getMobile();
        redisTemplate.boundValueOps(createDate).set(dayCount);


        //查询当月签到次数
        LambdaQueryWrapper<SignInfo> signInfoMonth=new LambdaQueryWrapper<>();
        signInfoMonth.eq(SignInfo::getCompanyId,CurrentUserHolder.get().getCompanyId());
        signInfoMonth.eq(SignInfo::getCreateMonth,signDTO.getCreateMonth());
        Integer month = signInfoMapper.selectCount(signInfoMonth);
        String monthCount = String.valueOf(month);

        //存储当月的签到次数到redis
        if(monthCount==null){
            throw new NcException(ResponseEnum.DATA_NOT_EXISTS);
        }

        //设置key=当天日期+手机号
        String createMonth = signDTO.getCreateMonth()+CurrentUserHolder.get().getMobile();
        redisTemplate.boundValueOps(createMonth).set(monthCount);

    }


    @Override
    public void exportSignData(String startTime, String endTime) throws IOException {
        if (startTime == null && endTime == null) {
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }
        //获取指定范围内的 日期列表
        List<Date> dateList = DateTimeUtil.getDatesBetweenTwoDate(startTime, endTime, DateTimeUtil.TIME_FORMAT_2);
        //转成字符串列表
        List<String> dateStr = dateList.stream()
                .map(date -> DateTimeUtil.dateToStr(date, DateTimeUtil.TIME_FORMAT_2)).collect(Collectors.toList());
        //获取成员列表
        List<SysCompanyUserDTO> companyUserDTOList = sysUserFeign.findAllCompanyUser().getData();
        //转换为签到对象
        List<SignPCListDTO> signPCListDTOS = new ArrayList<>();
        for (SysCompanyUserDTO companyUserDTO : companyUserDTOList) {
            SignPCListDTO signPCListDTO = new SignPCListDTO();
            signPCListDTO.setUsername(companyUserDTO.getUserName());
            SysDepartmentDTO departmentDTO = new SysDepartmentDTO();
            departmentDTO.setName(companyUserDTO.getDepartmentName());
            signPCListDTO.setDepartment(departmentDTO);
            signPCListDTO.setWorkNumber(companyUserDTO.getWorkNumber());
            signPCListDTO.setPost(companyUserDTO.getPost());
            signPCListDTO.setId(companyUserDTO.getId());
            signPCListDTOS.add(signPCListDTO);
        }

        for (SignPCListDTO signPCListDTO : signPCListDTOS) {

            //创建签到列表
            List<SignInfoDTO> signList = new ArrayList<>();
            for (String date : dateStr) {
                //获取每个员工的每天考勤列表
                LambdaQueryWrapper<SignInfo> queryWrapper = Wrappers.<SignInfo>lambdaQuery()
                        .eq(SignInfo::getCompanyId, CurrentUserHolder.get().getCompanyId())
                        .eq(SignInfo::getCompanyUserId, signPCListDTO.getId())
                        .eq(SignInfo::getCreateDate, date);
                //查询每天考勤次数
                Integer count = signInfoMapper.selectCount(queryWrapper);
                SignInfoDTO sign = new SignInfoDTO();
                sign.setSignDate(date);
                sign.setSignCount(count);
                signList.add(sign);

            }
            signPCListDTO.setSigns(signList);

        }

        //设置文件下载的头信息
        HttpServletResponse response =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setHeader("Content-disposition", "attachment;filename=Attendance.xlsx");
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setCharacterEncoding("utf-8");

        //写入Excel
        EasyExcel.write(response.getOutputStream())
                .excelType(ExcelTypeEnum.XLS) //文件的类型
                .head(handleHeader(signPCListDTOS)) //操作 表头
                .registerWriteHandler(new MyColumnStyle()) //设置列宽
                .registerWriteHandler(horizontalCellStyleStrategy()) //设置水平居中
                .sheet("考勤记录") //excel的工作表名称
                .doWrite(handleData(signPCListDTOS)); //操作 excel中的数据的

    }

    private List handleData(List<SignPCListDTO> signPCListDTOS) {
        List<List<String>> data = new ArrayList<>();
        //每个员工 应该对应 一行数据， 每行数据应该 是一个List集合
        for (SignPCListDTO signPCListDTO : signPCListDTOS) {
            List<String> list = new ArrayList<>();
            //添加数据
            //添加固定字段
            list.add(signPCListDTO.getUsername());
            list.add(signPCListDTO.getWorkNumber());
            list.add(signPCListDTO.getPost());
            list.add(signPCListDTO.getDepartment().getName());
            //添加 非固定字段(每天的考勤情况)
            List<SignInfoDTO> signList = signPCListDTO.getSigns();
            if (CollectionUtil.isNotEmpty(signList)) {
                for (SignInfoDTO signInfoDTO : signList) {

                    list.add(String.valueOf(signInfoDTO.getSignCount()));
                }
            }

            //组装数据
            data.add(list);
        }
        return data;

    }

    /**
     * 操作excel表头信息的
     * @param signPCListDTOS
     * @return
     */
    private List<List<String>> handleHeader(List<SignPCListDTO> signPCListDTOS) {
        List<List<String>> list = new ArrayList<>();
        //固定表头
        list.add(Arrays.asList("姓名"));
        list.add(Arrays.asList("工号"));
        list.add(Arrays.asList("部门"));
        list.add(Arrays.asList("职位"));
        //不固定表头(日期部分)
        if(CollectionUtil.isNotEmpty(signPCListDTOS)){
            //取出一个人的考勤记录
            SignPCListDTO listDTO = signPCListDTOS.get(0);

            //获取 每天的打卡情况
            List<SignInfoDTO> signs = listDTO.getSigns();

            for (SignInfoDTO sign : signs) {
                String signDate = sign.getSignDate();
                list.add(Arrays.asList(signDate));
            }
        }

        return list;
    }

    /**
     * 设置水平居中
     * @return
     */
    public HorizontalCellStyleStrategy horizontalCellStyleStrategy() {
        WriteCellStyle cellStyle = new WriteCellStyle();
        //设置字体居中
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        //设置字体样式
        WriteFont font = new WriteFont();
        font.setFontName("宋体");
        font.setFontHeightInPoints((short) 12);
        cellStyle.setWriteFont(font);

        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(cellStyle, cellStyle);

        return styleStrategy;


    }


}
